/************************************************************
 * AP_FLAGS.H                                      05.09.2006
 *
 * .
 ************************************************************/
#ifndef _AP_FLAGS_H_
#define _AP_FLAGS_H_

#pragma once

#ifdef _AP_XENON 
#define DS_FLAGS_CHECK(A, B) // disable static check for xenon, since XDK 11755 ADL doesn't work correctly
#else
#define DS_FLAGS_CHECK(A, B) (void)sizeof apFlagsImplFunc(A, B)
#endif

template <class T, class C = int>
class dsFLAGS {
public:
   dsFLAGS        (void) : val() {}

   template <class A>
   dsFLAGS        (A a) : val(a) { DS_FLAGS_CHECK(a, (T)val); }

   template <class A>
   inline dsFLAGS<T,C> & Set    (A a, BOOL set) { return set ? Set(a) : Clear(a); DS_FLAGS_CHECK(a, (T)val); }

   template <class A>
   inline dsFLAGS<T,C> & Set    (A a) { val |= a; DS_FLAGS_CHECK(a, (T)val); return *this; }

   template <class A>
   inline dsFLAGS<T,C> & Clear  (A a) { val &= ~a; DS_FLAGS_CHECK(a, (T)val); return *this; }

   inline void    Toggle       (T a) { val ^= a; }

   inline void    Fill        (T a) { val = a; }

   inline void    Clear       (void) { val = C(); }

   template <class A>
   inline bool    Is          (A a) const { return (val & a) != 0; DS_FLAGS_CHECK(a, (T)val); }

   template <class A>
   inline bool    IsEach      (A a) const { return (val & a) == a; DS_FLAGS_CHECK(a, (T)val); }

   template <class A>
   inline bool    IsToggled   (const dsFLAGS<T,C> & f, A a) const { return (val & a) != (f.val & a); DS_FLAGS_CHECK(a, (T)val); }

   T              Value       (void) const { return (T)val; }

private:
   C              val;
};

#define dsDECL_FLAGS_COMMON(NAME)                     \
   bool apFlagsImplFunc(NAME, NAME##_IMPL_PARENT);    \
   template <class A>                                 \
   inline NAME##_IMPL_PARENT operator | (NAME a, A b) { return (NAME##_IMPL_PARENT)((int)a | (int)b); DS_FLAGS_CHECK(b, (NAME##_IMPL_PARENT)a); } \
   template <class A>                                 \
   inline NAME##_IMPL_PARENT operator |= (NAME & a, A b) { return a = (NAME##_IMPL_PARENT)((int)a | (int)b); DS_FLAGS_CHECK(b, (NAME##_IMPL_PARENT)a); } \
   template <class A>                                 \
   inline NAME##_IMPL_PARENT operator & (NAME a, A b) { return (NAME##_IMPL_PARENT)((int)a & (int)b); DS_FLAGS_CHECK(b, (NAME##_IMPL_PARENT)a); } \
   inline NAME##_IMPL_PARENT operator ~ (NAME a)      { return (NAME##_IMPL_PARENT)(~(int)a); DS_FLAGS_CHECK(a, (NAME##_IMPL_PARENT)a); } \

#define dsDECL_FLAGS_TF(NAME, TYPE)                   \
   typedef NAME NAME##_IMPL_PARENT;                   \
   typedef dsFLAGS<NAME, TYPE> NAME##_F;              \
   typedef TYPE NAME##_IMPL_TYPE;                     \
   dsDECL_FLAGS_COMMON(NAME);                         \

#define dsDECL_FLAGS_T(NAME, LAST, TYPE)              \
   dsDECL_FLAGS_TF(NAME, TYPE)                        \
   const int NAME##_LAST = LAST;                      \
   CASSERT_MSG((unsigned)(LAST) <= (unsigned)(1 << (8*sizeof(NAME##_IMPL_TYPE)-1)), TYPE_BOUND);

#define dsDECL_FLAGS(NAME, LAST)                      \
   dsDECL_FLAGS_T(NAME, LAST, int);

#define dsDECL_FLAGS_8(NAME, LAST)                    \
   dsDECL_FLAGS_T(NAME, LAST, unsigned char);

#define dsDECL_FLAGS_16(NAME, LAST)                   \
   dsDECL_FLAGS_T(NAME, LAST, unsigned short);

#define dsDECL_FLAGS_S(NAME)                          \
   dsDECL_FLAGS_TF(NAME, int)

#define dsDECL_FLAGS_F(NAME)                          \
   dsDECL_FLAGS_TF(NAME, int)                         \

#define dsDECL_CHILD_FLAGS(NAME, PARENT, LAST)        \
   typedef PARENT##_IMPL_PARENT NAME##_IMPL_PARENT;   \
   PARENT apFlagsImplFunc(NAME);                      \
   typedef PARENT##_IMPL_TYPE NAME##_IMPL_TYPE;       \
   dsDECL_FLAGS_COMMON(NAME);                         \
   const int NAME##_LAST = LAST;                      \
   CASSERT_MSG((unsigned)(LAST) <= (unsigned)(1 << (8*sizeof(NAME##_IMPL_TYPE)-1)) && (LAST) >= PARENT##_LAST, TYPE_BOUND);

#define dsDECL_LOCAL_FLAGS(NAME, CLASS)               \
   typedef dsFLAGS<NAME> NAME##_F;                    \
   friend inline NAME operator | (NAME a, NAME b) { return (CLASS::NAME)((int)a | (int)b); } \
   friend inline NAME operator |= (NAME & a, NAME b) { return a = (CLASS::NAME)((int)a | (int)b); } \
   friend inline NAME operator & (NAME a, NAME b) { return (CLASS::NAME)((int)a & (int)b); } \
   friend inline NAME operator ~ (NAME a)         { return (CLASS::NAME)(~(int)a); } \
   friend bool apFlagsImplFunc(NAME, NAME);

// alternative flags

#define dsDECL_STATE_BEGIN_T(NAME, TYPE) \
struct NAME { \
   TYPE value; \
   enum { ZERO_LINE = __LINE__, TYPE_SIZE = 8 * sizeof(TYPE) };

#define dsDECL_STATE_BEGIN(NAME) \
   dsDECL_STATE_BEGIN_T(NAME, int)

#define dsDECL_STATE(NAME) \
   CASSERT(__LINE__ - ZERO_LINE - 1 < TYPE_SIZE); \
   enum { STATE_##NAME = 1 << (__LINE__ - ZERO_LINE - 1) }; \
   public: bool Is##NAME(void) const { return (value & STATE_##NAME) != 0; } \
   protected: void Set##NAME(bool set = true) { set ? value |= STATE_##NAME : value &= ~STATE_##NAME; }

#define dsDECL_STATE_END() \
protected:                 \
   ~NAME(void) {}          \
};

#endif // _AP_FLAGS_H_
